# ==============================================================================
# SCRIPT 02: DETERMINAÇÃO DO NÚMERO ÓTIMO DE CLUSTERS (K) - 2022
# ==============================================================================

# ------------------------------------------------------------------------------
# 1. OBJETIVO
# ------------------------------------------------------------------------------
# Este script utiliza duas técnicas (Método do Cotovelo e Análise de Silhueta)
# para determinar o número ideal de clusters (k) para a base de dados de 2022.
# A escolha correta de 'k' é fundamental para a qualidade da clusterização.

# ------------------------------------------------------------------------------
# 2. BIBLIOTECAS NECESSÁRIAS
# ------------------------------------------------------------------------------
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import silhouette_score
from scipy.spatial.distance import cdist
import warnings

warnings.filterwarnings("ignore")

# ------------------------------------------------------------------------------
# 3. CONFIGURAÇÕES E CONSTANTES
# ------------------------------------------------------------------------------

# Caminho do arquivo de entrada (gerado pelo script 01)
INPUT_PATH = "../2022_data/base_final_2022.csv"

# Caminhos dos arquivos de saída (gráficos)
ELBOW_PLOT_PATH = "../2022_data/grafico_metodo_cotovelo_2022.png"
SILHOUETTE_PLOT_PATH = "../2022_data/grafico_analise_silhueta_2022.png"

# Lista de segmentos de investimento (features para clusterização)
SEGMENTOS = [
    "Renda Fixa", "Renda Variável", "Investimentos no Exterior",
    "Investimentos Estruturados", "Fundos Imobiliários",
    "Empréstimos Consignados", "Disponibilidades Financeiras"
]

# Range de valores de k para testar
K_RANGE = range(2, 11) # Testaremos de 2 a 10 clusters

# ------------------------------------------------------------------------------
# 4. EXECUÇÃO DO SCRIPT
# ------------------------------------------------------------------------------

print("="*80)
print("INICIANDO DETERMINAÇÃO DO K ÓTIMO - 2022")
print("="*80)

# --- 4.1. Carregamento e Preparação dos Dados ---
print("\n1. Carregando e preparando os dados...")
df = pd.read_csv(INPUT_PATH)

# Selecionar apenas as colunas dos segmentos para a clusterização
X = df[SEGMENTOS]

# Padronizar os dados (essencial para K-Means)
# O StandardScaler transforma os dados para que tenham média 0 e desvio padrão 1.
# Isso evita que variáveis com escalas maiores dominem a análise.
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

print(f"  - {len(df)} RPPS carregados.")
print("  - Dados padronizados com sucesso.")

# --- 4.2. Método do Cotovelo (Elbow Method) ---
print("\n2. Executando o Método do Cotovelo...")

# Lista para armazenar as distorções (soma das distâncias quadradas)
distortions = []

for k in K_RANGE:
    # Cria e treina o modelo K-Means
    kmeanModel = KMeans(n_clusters=k, init='k-means++', n_init=10, random_state=42).fit(X_scaled)
    
    # Calcula a distorção e adiciona à lista
    # A distorção é a soma das distâncias euclidianas quadradas de cada ponto
    # ao seu centroide mais próximo. Quanto menor, mais compacto o cluster.
    distortions.append(sum(np.min(cdist(X_scaled, kmeanModel.cluster_centers_, 'euclidean'), axis=1)) / X_scaled.shape[0])
    print(f"  - Testando k={k}... Distorção: {distortions[-1]:.4f}")

# Plotar o gráfico do cotovelo
plt.figure(figsize=(10, 6))
plt.plot(K_RANGE, distortions, 'bx-')
plt.xlabel('Número de Clusters (k)')
plt.ylabel('Distorção Média')
plt.title('Método do Cotovelo para Determinação do k Ótimo (2022)')
plt.grid(True)
plt.savefig(ELBOW_PLOT_PATH)
print(f"  - Gráfico do Método do Cotovelo salvo em: {ELBOW_PLOT_PATH}")

# --- 4.3. Análise de Silhueta (Silhouette Analysis) ---
print("\n3. Executando a Análise de Silhueta...")

# Lista para armazenar os scores de silhueta
silhouette_scores = []

for k in K_RANGE:
    # Cria e treina o modelo K-Means
    kmeans = KMeans(n_clusters=k, init='k-means++', n_init=10, random_state=42).fit(X_scaled)
    labels = kmeans.labels_
    
    # Calcula o score médio de silhueta
    # O score de silhueta mede quão similar um objeto é ao seu próprio cluster
    # em comparação com outros clusters. Varia de -1 a 1.
    # Valores mais altos indicam clusters mais bem definidos.
    score = silhouette_score(X_scaled, labels, metric='euclidean')
    silhouette_scores.append(score)
    print(f"  - Testando k={k}... Score de Silhueta: {score:.4f}")

# Plotar o gráfico da silhueta
plt.figure(figsize=(10, 6))
plt.plot(K_RANGE, silhouette_scores, 'bx-')
plt.xlabel('Número de Clusters (k)')
plt.ylabel('Score Médio de Silhueta')
plt.title('Análise de Silhueta para Determinação do k Ótimo (2022)')
plt.grid(True)
plt.savefig(SILHOUETTE_PLOT_PATH)
print(f"  - Gráfico da Análise de Silhueta salvo em: {SILHOUETTE_PLOT_PATH}")

# --- 4.4. Conclusão ---
optimal_k_silhouette = K_RANGE[np.argmax(silhouette_scores)]
print("\n4. Conclusão:")
print("-" * 20)
print(f"O valor de 'k' que maximiza o Score de Silhueta é: {optimal_k_silhouette}")
print("Este é o número de clusters recomendado para a análise de 2022.")

print("\n" + "="*80)
print("DETERMINAÇÃO DO K ÓTIMO DE 2022 CONCLUÍDA!")
print("="*80)
